using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Ninject;
using Tetris.Interfaces;
using Tetris.Levels;
using Tetris.Utility;

namespace Tetris.Blocks
{
    public class BlockManager : IDrawableObject
    {
        private readonly GameGrid _grid;

        private Texture2D _texture;
        private IBlock _fallingBlock;
        private List<IBlock> _stoppedBlocks;
        private Random _random;
        private IInputManager _inputManager;
        private Rectangle destroyRectangle;

        public BlockManager(GameGrid grid)
        {
            _grid = grid;
        }

        public void Initialize(IGameManager gameManager)
        {
            _texture = gameManager.ServiceLocator.Get<IContentFactory>().GetTexture(Constants.Content.Graphics.TETRIS_BLOCK);
            _inputManager = gameManager.ServiceLocator.Get<IInputManager>();
            _stoppedBlocks = new List<IBlock>();
            _random = new Random();
        }

        public void Update(IGameManager gameManager)
        {
            if (_fallingBlock == null)
            {
                // spawn new block if there is no currently falling block
                _fallingBlock = CreateBlock();
            }
            else
            {
                ControlBlock(_fallingBlock);

                List<int> completedLines = _grid.MoveBlock(_fallingBlock);

                // block has stopped?
                if (_fallingBlock.HasStopped)
                {
                    _stoppedBlocks.Add(_fallingBlock);
                    _fallingBlock = null;
                }
                else
                {
                    _fallingBlock.Update(gameManager);
                }

                if (completedLines != null && completedLines.Count > 0)
                {
                    DestroyPartsFromLine(completedLines);
                    for (int index = 0; index < _stoppedBlocks.Count; index++)
                    {
                        if (_stoppedBlocks[index].Tiles.Count == 0) _stoppedBlocks.RemoveAt(index);
                    }
                }
            }
        }

        private void DestroyPartsFromLine(List<int> rows)
        {
            foreach (IBlock stoppedBlock in _stoppedBlocks)
            {
                for (int i = stoppedBlock.Tiles.Count-1; i >= 0; i--)
                {
                    ITile tile = stoppedBlock.Tiles[i];
                    if (rows.Contains(tile.GetRow()))
                    {
                        stoppedBlock.Tiles.RemoveAt(i);
                    } else if (tile.GetRow() < rows.Min())
                    {
                        tile.Position = new Vector2(tile.Position.X, tile.Position.Y + _texture.Height * rows.Count);
                        tile.SetRow(tile.GetRow() + rows.Count);
                    }
                }
            }
        }

        private void ControlBlock(IBlock fallingBlock)
        {
            if (_grid.CanRotate(fallingBlock) && _inputManager.IsBlockRotated())
            {
                _fallingBlock.Rotate();
            }
            if (_grid.CanMoveLeft(fallingBlock) && _inputManager.IsBlockMovedToLeft())
            {
                _fallingBlock.Position = new Vector2(_fallingBlock.Position.X - _texture.Width, _fallingBlock.Position.Y);
            }
            if (_grid.CanMoveRight(fallingBlock) && _inputManager.IsBlockMovedToRight())
            {
                _fallingBlock.Position = new Vector2(_fallingBlock.Position.X + _texture.Width, _fallingBlock.Position.Y);
            }
            _fallingBlock.FallDelay = _inputManager.IsBlockMovedDown() ? 50 : 500;
        }

        private IBlock CreateBlock()
        {
            int r = (int)(_random.NextDouble() * 255);
            int g = (int)(_random.NextDouble() * 255);
            int b = (int)(_random.NextDouble() * 255);

            AbstractBlock block;
            Random rand = new Random();
            int randInt = rand.Next(7);
            if (randInt == 6)
            {
                block = new TBlock(_texture, new Color(r, g, b, 255), new Vector2(142, 33), 500);
            } else if (randInt == 5)
            {
                block = new LBlock(_texture, new Color(r, g, b, 255), new Vector2(155 - _texture.Width / 2, 33+_texture.Height/2), 500);
            }
            else if (randInt == 4)
            {
                block = new MirroredLBlock(_texture, new Color(r, g, b, 255), new Vector2(155 - _texture.Width / 2, 33 + _texture.Height / 2), 500);
            }
            else if (randInt == 3)
            {
                block = new ZBlock(_texture, new Color(r, g, b, 255), new Vector2(155 - _texture.Width, 33), 500);
            }
            else if (randInt == 2)
            {
                block = new MirroredZBlock(_texture, new Color(r, g, b, 255), new Vector2(155 - _texture.Width, 33), 500);
            }
            else if (randInt == 1)
            {
                block = new BarBlock(_texture, new Color(r, g, b, 255), new Vector2(155 - _texture.Width/2, 33 + _texture.Height/2), 500);
            }
            else
            {
                block = new SquareBlock(_texture, new Color(r, g, b, 255), new Vector2(155, 33), 500);
            }
            return block;
        }

        public void Draw(SpriteBatch spriteBatch, IGameManager gameManager, GameTime gameTime)
        {
            spriteBatch.Begin();

            if (_fallingBlock != null) _fallingBlock.Draw(spriteBatch, gameManager, gameTime);
            foreach (IBlock block in _stoppedBlocks)
            {
                block.Draw(spriteBatch, gameManager, gameTime);
            }
            spriteBatch.End();
        }
    }
}